using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Web.Caching;
using System.IO;
using System.Xml.Serialization;
using System.Xml;

namespace SharePointing.Features.MetaTags.Common
{
    public struct Lists
    {
        public const string METATAGTYPELIST = "MetaTagType";
        public const string METATAGLIST = "MetaTags";
    }

    public struct CharacterSequences
    {
        public const string LESSTHAN = "&lt;";
        public const string GREATERTHAN = "&gt;";
        public const string QUOTE = "&quot;";
    }

    public struct ContentTypes
    {
        public const string METATAGTYPECONTENTTYPE = "MetaTagType";
        public const string METATAGCONTENTTYPE = "MetaTag";
        public const string ROOTPUBLISHINGCONTENTTYPE = "Page";
    }
    public struct SiteColumns
    {
        public const string METATAGLOOKUPFIELDNAME = "MetaTag";
        public const string METATAGTYPEFIELDNAME = "MetaTagName";
        public const string METATAGVALUEFIELDNAME = "MetaTagValue";
        public const string METATAGPAGETAGS = "MetaTagPageTags";
        public const string METATAGCOLUMNGROUP = "MetaTags Custom Columns";
    }

    public struct CacheKeys
    {
        public const string SITETAGS = "MetaTags_Site";
    }

    public struct MetaTags
    {
        public const string AUTHOR = "author";
        public const string KEYWORDS = "keywords";
        public const string DESCRIPTION = "description";
    }

    public struct Settings
    {
        public const string ASSEMBLYNAME = "SharePointing.Features.MetaTags, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eaf5ef06195f8257";
        public const string RECEIVERNAME = "SharePointing.Features.MetaTags.MetaTagEventReceiver";

    }

    public class Utilities
    {
        public static string GetMetaTags()
        {
            // TODO: ADD CACHING SO WE DON'T HIT THE LIST ON EACH PAGE
            string _return = String.Empty;
            PageTag pageTag = new PageTag();

            try
            {
                // Grab the PageTags from the page
                string xmlString = SPContext.Current.ListItem.GetFormattedValue("MetaTagPageTags");
                if (String.IsNullOrEmpty(xmlString) == false)
                {
                    // There is a value stored in the field, let's deserialize it
                    pageTag = DeserializePageTag(xmlString);
                }  

                //if(HttpContext.Current.Cache[CacheKeys.SITETAGS] == null)
                //{
                    using (SPSite oSiteCollection = new SPSite(SPContext.Current.Site.Url))
                    {
                        using (SPWeb oRootWeb = oSiteCollection.RootWeb)
                        {
                            SPList oList = oRootWeb.Lists[Common.Lists.METATAGLIST];
                            foreach(SPListItem item in oList.Items)
                            {
                                string _fieldName = GetLookupValue(item[Common.SiteColumns.METATAGLOOKUPFIELDNAME].ToString());
                                string _fieldValue = item[Common.SiteColumns.METATAGVALUEFIELDNAME].ToString();
                                
                                switch(_fieldName)
                                {
                                    case Common.MetaTags.AUTHOR:
                                        // Is the Author Tag, Check our Page Tag to see which we want to insert
                                        if(pageTag.HasAuthor())
                                        {
                                            // Add the author from the PageTag
                                            _return += string.Format(@"<META name=""{0}"" content=""{1}"">", Common.MetaTags.AUTHOR, pageTag.Author);
                                        }
                                        else { _return += string.Format(@"<META name=""{0}"" content=""{1}"">", _fieldName, _fieldValue); }
                                        break;
                                    case Common.MetaTags.DESCRIPTION:
                                        // Is description Tag - always use from page if present
                                        if(pageTag.HasDescription())
                                        {
                                            _return += string.Format(@"<META name=""{0}"" content=""{1}"">", Common.MetaTags.DESCRIPTION, pageTag.Description);
                                        }
                                        else { _return += string.Format(@"<META name=""{0}"" content=""{1}"">", _fieldName, _fieldValue); }
                                        break;
                                    case Common.MetaTags.KEYWORDS:
                                        // Keywords
                                        if(pageTag.HasKeywords())
                                        {
                                            if(pageTag.Override)
                                            {
                                                // output the page tags ONLY
                                                _return += string.Format(@"<META name=""{0}"" content=""{1}"">", Common.MetaTags.KEYWORDS, pageTag.Keywords);
                                            }
                                            else
                                            {
                                                // Merge the global page tags with the ones for the page
                                                _return += string.Format(@"<META name=""{0}"" content=""{1}"">", Common.MetaTags.KEYWORDS, _fieldValue + "," + pageTag.Keywords);
                                            }
                                        }
                                        else
                                        {
                                            // return the globals only
                                            _return += string.Format(@"<META name=""{0}"" content=""{1}"">", Common.MetaTags.KEYWORDS, _fieldValue);
                                        }
                                        break;
                                    default:
                                        // Normal processing.. not one of the tags we care about.
                                        _return += string.Format(@"<META name=""{0}"" content=""{1}"">", _fieldName, _fieldValue);
                                        break;
                                }
                                //_return += string.Format(@"<META name=""{0}"" content=""{1}"">",_fieldName,_fieldValue);
                            }
                        }
                    }
                    // Save the result to the Cache
                    //HttpContext.Current.Cache[CacheKeys.SITETAGS] = _return;
                //}
                //else
                //{
                //    // Get the value from the cache
                //    _return = (string)HttpContext.Current.Cache[CacheKeys.SITETAGS];
                //}
             
            }
            catch(Exception ex)
            {
                _return += "<METATAGERROR>" + ex.Message + "</METATAGERROR>";
            }

            return _return;
        }

        public static string GetLookupValue(string input)
        {
            // When you retrieve the value of a lookup field, it is returned as ID;# instead of
            // just the value that we want. This method returns just the portion
            // to the right of the # character
            string _delimiter = "#";
            // get position of the delimiter
            int index = input.IndexOf(@_delimiter);
            input = input.Substring(index + 1);
            return input;

        }

        public static void ClearMetaTagCache()
        {
            // Clears the ASP.NET Cache for the MetaTags
            HttpContext.Current.Cache[CacheKeys.SITETAGS] = null;
        }

        public static String UTF8ByteArrayToString(Byte[] characters)
        {

            UTF8Encoding encoding = new UTF8Encoding();
            String constructedString = encoding.GetString(characters);
            return (constructedString);
        }

        public static Byte[] StringToUTF8ByteArray(String pXmlString)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            Byte[] byteArray = encoding.GetBytes(pXmlString);
            return byteArray;
        }

        public static String SerializePageTag(PageTag pageTag)
        {
            try
            {
                String XmlizedString = null;
                MemoryStream memoryStream = new MemoryStream();
                XmlSerializer xs = new XmlSerializer(typeof(PageTag));
                XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
                xs.Serialize(xmlTextWriter, pageTag);
                memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
                XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
                return XmlizedString;
            }
            catch (Exception e) { return null; }
        }

        public static PageTag DeserializePageTag(String pXmlizedString)
        {
            XmlSerializer xs = new XmlSerializer(typeof(PageTag));
            MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(CleanCharacterSequences(pXmlizedString)));
            XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
            return (PageTag)xs.Deserialize(memoryStream);
        }

        public static string CleanCharacterSequences(string stringToClean)
        {
            // This is NOT the way to do this but I had to work around it somehow...
            // needed this because the serialized text is changed and will not 
            // deserialize when we pull it back out of the field. SharePoint translates the characters
            stringToClean = stringToClean.Replace(Common.CharacterSequences.GREATERTHAN, ">");
            stringToClean = stringToClean.Replace(Common.CharacterSequences.LESSTHAN, "<");
            stringToClean = stringToClean.Replace(Common.CharacterSequences.QUOTE, "\"");
            return stringToClean;
        }
    }
}
